home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / REFERENC / TPR / TPR3C.TXT < prev    next >
Text File  |  1992-10-19  |  61KB  |  1,498 lines

  1.                    Chapter 3
  2.                  - continued -
  3.                  - Part 3 of 3 parts -
  4.                                     of the
  5.                             Turbo Pascal Reference
  6.  
  7.                            The Turbo Pascal Language
  8.  
  9.  
  10. This chapter is part of the Turbo Pascal Reference electronic freeware book (C)
  11. Copyright 1992 by Ed Mitchell.  This freeware book contains supplementary
  12. material to Borland Pascal Developer's Guide, published by Que Corporation,
  13. 1992.  However, Que Corporation has no affiliation with nor responsibility for
  14. the content of this free book.  Please see Chapter 1 of the Turbo Pascal
  15. Reference for important information about your right to distribute and use this
  16. material freely.  If you find this material of use, I would appreciate your
  17. purchase of one my books, such as the Borland Pascal Developer's Guide or
  18. Secrets of the Borland C++ Masters, Sams Books, 1992.  Thank you.
  19.  
  20. Functions
  21. Syntax:
  22.  
  23.      function <identifier> <parameter list> : <data type>
  24.  
  25. Examples:
  26.  
  27.      function Minimum ( A, B : Integer ) : Integer;
  28.      { Returns the value of the smaller number A, or B }
  29.      begin
  30.        if A < B then
  31.           Minimum := A
  32.        else { if B < A or B = A then }
  33.           Minimum := B;
  34.      end;
  35.  
  36.      { Example of a function returning a String value }
  37.      function LowerCase (S : String ) : String;
  38.      { Convert string S to lower case, returning the result }
  39.  
  40.      var
  41.        I : Integer;
  42.      begin
  43.        for I := 1 to length(S)  do
  44.           if ((S[I]>='A') and (S[I]<='Z'))  then
  45.             S[I] :=  Chr( Ord( S[I] ) + 32 );
  46.        LowerCase := S;
  47.      end; { LowerCase }
  48.  
  49. Description:
  50.      A function is similar to a procedure, except that a function is called
  51. from within an expression and it returns a value that is then used in
  52. evaluating the overall expression.  Functions can have both value and variable
  53. parameters, and may be declared as near, far, forward, and external, or may be
  54. implemented entirely in assembly language.  Functions may not, however, be
  55. declared as interrupt functions.
  56.  
  57.  
  58. Calling a function
  59.      The function is called by appearing within an expression, such as,
  60.  
  61.      I := Min ( X1, X2 ) + 1;
  62.  
  63.  
  64. Assigning a value to a function identifier
  65.      The function's block definition must include a statement that assigns a
  66. value to the function's identifier.  This is how a function gets a value that
  67. it can return to the caller.  If you omit this assignment statement, or the
  68. assignment statement does not get executed as a result of conditional
  69. statements in the function's code, then the function returns an undefined or
  70. potentially random value.  If during the course of debugging a program you find
  71. your functions returning erratic values, be certain that the function
  72. identifier is  correctly assigned a value.
  73.      Examples of assigning values to function identifiers appear above in the
  74. Min and LowerCase functions.
  75.  
  76.  
  77. Acceptable Function Return Values
  78.      The data type that a function returns can be any of the following:
  79.  
  80.        Any ordinal value, including Boolean, Byte, Char, Smallint, Integer,
  81.        Word, Longint, and enumarated data types and user defined sub range
  82.        types.
  83.  
  84.        Real, Single, Double, Extended and Comp data types,
  85.  
  86.        Pointer values
  87.  
  88.        Strings.
  89.  
  90.  
  91. Functions may not return records or sets, although they may return pointers to
  92. records or sets.
  93.  
  94.  
  95. Recursive functions
  96.      Functions may call themselves.  Such a function is called a recursive
  97. function.  A popular and simple example of a recursive function is a function
  98. that computes the factorial of a number.  The factorial of a number n, is n *
  99. (n-1) * (n-2) ... until n reaches 1.  For example, the factorial of 5, is 5 * 4
  100. * 3 * 2 * 1, which equals 120.  An illustration of how this might be solved
  101. recursively is shown in listing 3.10.  (Actually, its not necessary to use a
  102. recursive function to compute a factorial;  this method is used here for
  103. illustration only.)
  104.  
  105. Listing 3.10.  An example of a recursive function.
  106.    1  program DemoRecursion; {DEMORECU.PAS}
  107.    2  
  108.    3  function Factorial ( n : real ) : real;
  109.    4  begin
  110.    5    if n = 1 then
  111.    6      Factorial := 1
  112.    7    else
  113.    8      Factorial := N * Factorial ( N - 1.0 );
  114.    9  end;
  115.   10  
  116.   11  var
  117.   12    X : Real;
  118.   13  
  119.   14  begin
  120.   15    Write('Enter a number:  ');
  121.   16    Readln( X );
  122.   17    Writeln;
  123.   18    Writeln('Factorial of ',X,' = ', Factorial ( X ) );
  124.   19    Writeln;
  125.   20    Write('Press Enter to finish.');
  126.   21    Readln;
  127.   22  end.
  128.   23  
  129.  
  130. Important note:  The effect of short-circuit evaluation on functions
  131.      By default, Turbo Pascal generates short-circuit evaluation code, so it is
  132. possible that a function may not be called within a particular expression.  For
  133. example, consider a function defined as:
  134.  
  135.      function ValueInRange ( X1 : Integer ) : Boolean;
  136.      begin
  137.        ...
  138.        if  X1 > 0  then
  139.           ValueInRange := True
  140.        else
  141.           ValueInRange := False;
  142.        if  X1 < LowestCoordinate  then
  143.           LowestCoordinate := X1;
  144.      end;
  145.  
  146. In this function, a global variable LowestCoordinate may have its value changed
  147. during the course of execution.  If this function is called in an expression
  148. such as,
  149.  
  150.      if (X1<>X2) and ValueInRange(X1)  then ...
  151.  
  152. In normal short-circuit evaluation, if X1 is not equal to X2, then the
  153. remainder of the expression will not be evaluated.  If your code depends upon
  154. the value of LowestCoordinate being set as a side effect of the ValueInRange
  155. function call, this may result in an error.  In general, it is best to avoid
  156. side-effects within functions and procedures, but if you must make use of
  157. side-effect such as this, you should disable short-circuit evaluation to force
  158. the entire expression to be fully evaluated.  See the section "Compiler
  159. Directives" for more information on using the {$B+} option to enable full
  160. expression evaluation.
  161.  
  162.  
  163. Procedures and Functions as Parameters
  164.      A procedure or function may itself be passed to another procedure or
  165. function as a parameter value.  To pass a procedure or function as a parameter
  166. requires that a type declaration define a procedure type that matches the
  167. appropriate procedure or function header.  This type becomes the parameter type
  168. used in the procedure parameter list.  Listng 3.11 demonstrates the use of a
  169. procedure parameter.
  170.      Note that the type declaration describes the procedure or function that is
  171. called, and does not include the actual procedure or function identifier.
  172.  
  173. Listing 3.11.  Using a procedure type as a procedure parameter.
  174.    1  Program ProcParameter;{PROCPARM.PAS}
  175.    2  { Demonstrates using a procedure type as a procedure
  176.        parameter }
  177.    3  
  178.    4  type
  179.    5    FormatProc = procedure ( X : Integer );
  180.    6  
  181.    7  
  182.    8  const
  183.    9    MaxListSize = 15;
  184.   10    Values: array[1..MaxListSize] of Integer =
  185.   11       (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
  186.   12  
  187.   13  
  188.   14  
  189.   15  function Hexadecimal ( X: Word ) : String;
  190.   16  var
  191.   17    HiByte, LoByte : Word;
  192.   18  
  193.   19    function HexConvert( B : Byte ) : String;
  194.   20    const
  195.   21      HexTable : Array[0..15] of Char =
  196.   22        ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  197.   23         'A', 'B', 'C', 'D', 'E', 'F');
  198.   24    begin
  199.   25      HexConvert := HexTable[B div 16] + HexTable[B and 15];
  200.   26    end;
  201.   27  
  202.   28  begin
  203.   29    HiByte := X Div 256;
  204.   30    LoByte := X and 255;
  205.   31    Hexadecimal := HexConvert( HiByte ) + HexConvert(LoByte);
  206.   32  end;
  207.   33  
  208.   34  
  209.   35  
  210.   36  procedure PrintInteger( X : Integer ); far;
  211.   37  begin
  212.   38  
  213.   39    Writeln( X : 5 );
  214.   40  
  215.   41  end;
  216.   42  
  217.   43  
  218.   44  
  219.   45  procedure PrintHex( X : Integer ); far;
  220.   46  begin
  221.   47  
  222.   48    Writeln ( Hexadecimal ( X ) );
  223.   49  
  224.   50  
  225.   51  end;
  226.   52  
  227.   53  
  228.   54  procedure PrintPercent( X : Integer ); far;
  229.   55  begin
  230.   56  
  231.   57    Writeln( X, '%');
  232.   58  
  233.   59  end;
  234.   60  
  235.   61  
  236.   62  procedure Traverse ( Proc : FormatProc );
  237.   63  var
  238.   64    I : Integer;
  239.   65  begin
  240.   66    for I := 1 to MaxListSize do
  241.   67      Proc( Values[I] );
  242.   68  end;
  243.   69  
  244.   70  
  245.   71  begin
  246.   72    Traverse ( PrintInteger );
  247.   73    Traverse ( PrintHex );
  248.   74    Traverse ( PrintPercent );
  249.   75  
  250.   76    readln;
  251.   77  
  252.   78  end.
  253.  
  254.  
  255. Conditional Compilation
  256.      While not specifically a part of the Pascal programming language, Turbo
  257. Pascal provides special compiler directives to create different executable
  258. programs from a single source.  These features are called conditional
  259. compilation directives.  The basic statements are,
  260.  
  261.      {$IFxxxx} <Pascal statements> {$ENDIF}
  262.  
  263. and
  264.  
  265.      {$IFxxxx} <Pascal statements> {$ELSE} <Pascal statements> 
  266.      {$ENDIF}
  267.  
  268. where the {$IFxxxx} may be one of the following conditional tests,
  269.  
  270.           {$IFDEF symbol}:  If symbol is defined, then compile the statements
  271.        between the {$IFDEF} and the {$ENDIF} or {$ELSE}.  
  272.  
  273.           {$IFNDEF symbol}:  If symbol is not defined, then compile the
  274.        statements between the {$IFNDEF} and the {$ENDIF} or {$ELSE}.
  275.  
  276.           {$IFOPT switch}: Tests the state of compiler directive switches (such
  277.        as $R+ or $I-, for instance).  For example, {$IFOPT I+} compiles the
  278.        following Pascal statements if the $I options is currently set, while
  279.        {$IFOPT I-} compiles the following statements if the $I options is not
  280.        set.
  281.  
  282.      These statements may be embedded into Pascal source code like any other
  283. program comment.  Symbols used in conditional compilation statements are
  284. created with the {$DEFINE symbol} directive, and eliminated or undefined with
  285. the {$UNDEF symbol} directive.  
  286.      A frequent use of conditional compilation is to embed debugging code into
  287. a program during development.  By changing a single option, all of the debug
  288. code can be eliminated from the final compilation, but switched back on if bugs
  289. are detected or new program features are added later.  Listing 3.12 illustrates
  290. the use of the conditional compiler directives in a section of code.
  291.  
  292. Listing 3.12.  This program section shows how conditional compilation might be
  293. used to add debug statements into a program.
  294.    1  {$DEFINE DEBUGGING} {DEBUG.PAS}
  295.    2  {$IFDEF DEBUGGING}
  296.    3  procedure DebugTrace( S : String; X : Integer );
  297.    4  begin
  298.    5    Writeln( DebugFile, S,', ', X );
  299.    6  end;
  300.    7  {$ENDIF}
  301.    8  
  302.    9  {---------------------------------------------------------}
  303.   10  function  UpperCase ( S : String ) : String;
  304.   11  var
  305.   12    I : Integer;
  306.   13  begin
  307.   14    {$IFDEF DEBUGGING}
  308.   15    DebugTrace('UpperCase', 0 );
  309.   16    {$ENDIF}
  310.   17    for  I := 1 to Length( S )  do
  311.   18      S[I] := UpCase ( S[I] );
  312.   19    UpperCase := S;
  313.   20  end; { UpperCase }
  314.   21  {------------------------------------------------------------}
  315.   22  function HeapFunc ( Size : Word ) : integer;
  316.   23  begin
  317.   24    {$IFDEF DEBUGGING}
  318.   25    DebugTrace('HeapFunc', 0 );
  319.   26    {$ENDIF}
  320.   27  
  321.   28    HeapFunc := 1;
  322.   29  end;
  323.   30  
  324.   31  {-----------------------------------------------------------}
  325.   32  function LowerCase (S : String ) : String;
  326.   33  Var
  327.   34    I : Integer;
  328.   35  begin
  329.   36    {$IFDEF DEBUGGING}
  330.   37    DebugTrace('LowerCase', 0 );
  331.   38    {$ENDIF}
  332.   39  
  333.   40    for I := 1 to length(s)  do
  334.   41      if ((S[I]>='A') and (S[I]<='Z'))  then
  335.   42        S[I] :=  Chr( Ord( S[I] ) + 32 );
  336.   43    LowerCase := S;
  337.   44  end;
  338.   45  
  339.   46  {------------------------------------------------------------}
  340.   47  Function Max ( A, B : Integer ) : Integer;
  341.   48  Begin
  342.   49    {$IFDEF DEBUGGING}
  343.   50    DebugTrace('Max', 0 );
  344.   51    {$ENDIF}
  345.   52  
  346.   53    IF A>B THEN Max := A Else Max := B;
  347.   54  End; {Max}
  348.   55  
  349.  
  350. Important Note: Conditional Symbols
  351.      The symbols defined by {$DEFINE} must follow the same rules as those
  352. applied to normal Pascal language identifiers.  However, symbols defined by
  353. {$DEFINE} are completely unrelated to identifiers declared within Pascal
  354. programs.  For example, it is permissible to have,
  355.  
  356.      {$DEFINE Debug}
  357.      var
  358.        Debug : Boolean;
  359.  
  360. because these identifiers have no relation to one another.  Similarly, if you
  361. wrote,
  362.  
  363.      {$DEFINE Options}
  364.      if  Options  then ....
  365.  
  366. you would receive an error because the symbol Options used in the if-then
  367. statement has not been declared (at least as a Pascal identifier).
  368.      In addition to the {$DEFINE} directive, symbol may also be defined with
  369. the /D command line option (See Chapter 10, "Turbo Pascal Standalone Programs"
  370. in the Borland Pascal Developer's Guide) or the Conditional Defines option of
  371. the IDE (see chapter 2 of the Turbo Pascal Reference).
  372.  
  373.  
  374. Built-in Conditional Compilation Symbols
  375.      Turbo Pascal automatically defines the following 4 conditional compilation
  376. symbols:
  377.  
  378.        VER60:  Turbo Pascal automatically defines this symbol, meaning that
  379.        this is version 6.0 of the Turbo Pascal compiler.
  380.  
  381.        VER70:  Turbo Pascal defines this symbol when you are compiling under
  382.        version 7.0 of the Turbo Pascal compiler.
  383.  
  384.        MSDOS:  Defined for all MS-DOS versions of the Turbo Pascal.
  385.  
  386.        CPU86:  Defined for all versions of Turbo Pascal running on 80x86
  387.        microprocessors.
  388.  
  389.        CPU87:  If, at compile time, the compiler detects an 80x87 math
  390.        coprocessor, this symbol is automatically defined.  When used in
  391.        conjunction with {$N}, this symbol can be used to set the floating-point
  392.        libraries for use with or without the 80x87 coprocessor.  Example:
  393.             {$IFDEF CPU87} {$N+} {$ELSE} {$N-} {$ENDIF}
  394.  
  395.  
  396. Compiler Directives
  397.      Compiler directives are special program comments that select compiler
  398. options.  Two types of compiler directives are provided (in addition to the
  399. conditional compiler options just described):
  400.  
  401.        Switch directives:  These are written in the form {$I+} or {$I-}, where
  402.        $I specifies a particular option, and a + symbol switches the option on,
  403.        and a minus - symbol switches the option off.
  404.  
  405.        Parameter directives:  Parameters specify a value to the option, such
  406.        as, {$I filename} to include another file during compilation.
  407.  
  408. Compiler options may be set by embedding compiler directives into the source
  409. text, or may be set globally using the IDE's Options menu, Compiler selection. 
  410. (See Chapter 2, in the section titled "Options Menu/Compiler)".  Many of the
  411. options may also be specified as command line options to the command line
  412. version of the compiler (See Chapter 10 in the Borland Pascal Developer's
  413. Guide).
  414.      Table 3.3 describes the embedded compiler directive options.
  415.  
  416.  
  417. Table 3.3.  The $ compiler directives.
  418.  
  419. Word align data:  $A, default $A+
  420.           Normally, the compiler generates byte aligned data assignments. 
  421.           Setting this option forces the compiler to generate word aligned
  422.           assignments, so that if one value ends on an odd byte, the compiler
  423.           will skip a byte to insure that the next assignment is made to an
  424.           even byte position.  The 80x86 CPUs, including the 8086, operate
  425.           slightly faster when fetching word-aligned data than when fetching
  426.           byte aligned data.
  427.  
  428. Complete Boolean evaluation:  $B, default $B-
  429.           The Turbo Pascal compiler normally performs short-circuited boolean
  430.           expression evaluation.  This means that as the expression is
  431.           evaluated at run time, as soon as the result is definitely known, the
  432.           remainder of the expression will be ignored.  This is particularly
  433.           useful in testing an index value before using it in an array
  434.           subscript, in for example,
  435.             if Index < 10  and (X[Index]=9)  then...
  436.           If  Index is 10 or greater, the program will not execute the array
  437.           subscript.  However, by enabling this compiler option, $B+, you can
  438.           force the program to always fully evaluate the expression.  This
  439.           might be used when calling a function within an expression, and your
  440.           program must use an intended side affect of the function call.
  441.  
  442. Debug information: $D, default $D+
  443.           If you are going to use either the built-in or the standalone
  444.           debuggers, set this option on so that the compiler will generate
  445.           internal line number tables to assist in matching generated code to
  446.           program source statements.  You can selectively disable debug
  447.           information on a per unit basis; such units cannot have their
  448.           procedures or functions traced into.
  449.  
  450. Emulation: $E, default $E+
  451.           When this option is set, the compiler will link in routines that
  452.           automatically detect the prescence or abscence of the floating point
  453.           processors.  If the math processor is installed, then it will be
  454.           used; if the math processor is not installed, the routines that
  455.           simulate the processor are called instead.  To generate code for all
  456.           types of machines, regardless of whether or not they have a floating
  457.           point chip, select both the 8087/80287 and the Emulation options.  If
  458.           you know that the target machine definitely has a coprocessor, then
  459.           disable this option but enable the 8087/80287 option.  See Table 3.1
  460.           and A Note on the Use of Floating Point Values earlier in this
  461.           chapter for additional information concerning Pascal floating point
  462.           data types and use of the math coprocessor and emulation libraries.
  463.  
  464. Force far calls: $F, default $F-
  465.           Normally, the compiler generates near calls for all procedure and
  466.           functions in the current file, and far calls for all procedures or
  467.           functions appearing in the interface portion of a unit.  You can
  468.           force far call generation by selecting this option.  Also see the
  469.           section @ and Procedures and Functions, for examples using the far
  470.           option for passing procedures and functions as parameters to
  471.           procedures.
  472.  
  473. 286 instructions: $G, default $G-
  474.           When set, the compiler generates code using the additional
  475.           instructions available in the 80286 instruction set.  Programs
  476.           created with this option will not run on 8088 or 8086 processors.
  477.  
  478. I/O checking: $I, default $I+
  479.           The default setting is to have the compiler check for input/output
  480.           errors on all input/output operations, such as reading or writing to
  481.           a file.  However, usually you will want to have your own checks for
  482.           such errors so you will want to disable this option.  Usethe $I-
  483.           compiler directive to locally disable I/O checking.  You can then
  484.           check the value of the IOResult function to determine if an error
  485.           occurred.  See Checking for File-related Errors, later in this
  486.           chapter.
  487.  
  488. Include File parameter directive: $I filename
  489.           This form of the $I compiler directive causes the specified filename
  490.           to be incorporated directly into the Pascal source text at the
  491.           location of the directive.  Its operation is as if the included file
  492.           has been typed at this location in the original source file.  The $I
  493.           is a convenient way of breaking large source files into smaller, more
  494.           manageable pieces.  Included files may themselves include other
  495.           files, up to a maximum nesting depth of 15 files.  The only
  496.           restriction is that all statements between a begin - end pair must
  497.           appear in the same source file.  For example,
  498.             Program Demo;
  499.             var
  500.             ...
  501.             {$I IOPROCS.PAS}
  502.             {$I ERRCODE.PAS}
  503.             begin { Demo }
  504.                     ...
  505.             end; { Demo }
  506.  
  507. Local symbols: $L, default $L+
  508.           When set, the compiler builds a table of all local variables and
  509.           constant identifiers for use during debugging session. If {$D-} is in
  510.           effect, disabling the generation of debug information, then {$L+} is
  511.           ignored.  See also the $D option, above.
  512.  
  513. Link object file parameter directive: $L filename
  514.           When using external assembly language routines, the $L parameter
  515.           directive tells the compiler which .OBJ file contains the externally
  516.           defined routine.
  517.  
  518. Memory allocation parameter directive: $M stacksize, heapmin, heapmax,         
  519. Default $M 16384, 0, 655360
  520.           The $M parameter directives specifies, for a Program module, how much
  521.           memory to allocate to the program stack, and the minimum and maximum
  522.           size permitted for the heap memory allocation.  The default values
  523.           specify a stack size of 16k bytes, and a maxium heap allocation up to
  524.           640k bytes.  During program initialization, the heap memory is
  525.           allocated from whatever is left over after loading the program code
  526.           and setting aside space for global variables and the stack.  As a
  527.           result, the actual heap allocation may be less than the maximum value
  528.           specified in this directive.
  529.             The stacks size may be set to any value from 1,204 to 65,520 bytes.
  530.           The heap values may be set anywhere from 0 to 655,360 bytes.
  531.  
  532. 8087/80287:  $N, default $N-
  533.           When set, this results in the compiler generating code for the 8087
  534.           or 80x87 math coprocessor chip.  This option is often used in
  535.           conjunction with the Emulation mode option (see above).  Also see See
  536.           Table 3.1 and A Note on Floating Point Values earlier in this chapter
  537.           for additional information concerning Pascal floating point data
  538.           types and use of the math coprocessor and emulation libraries.
  539.  
  540. Overlays allowed: $O, default $O-
  541.           You must set this option for all units that will subsequently be
  542.           overlaid.  Its also okay to set this option for units that are not
  543.           overlaid as it merely adds some additional run time checks to the
  544.           generated code.  Chapter 3, "Overlays" in the Borland Pascal
  545.           Developer's Guide describes the creation and use of overlay programs.
  546.  
  547. Overlay unit name parameter directive: $O unitname
  548.           When a Program module will use overlay units, immediately after the
  549.           uses statement, the program must specify which units should be placed
  550.           in a separate .OVR overlay file instead of the main .EXE program
  551.           executable file.  All such overlay units must be compiled with the
  552.           $O+ option.  See Chapter 2, "Units and Dynamic Link Libraires" for
  553.           information on creating and using program units, and chapter 3,
  554.           "Overlays" for information on using overlays, both in the Borland
  555.           Pascal Developer's Guide.
  556.  
  557. Range checking: $R, default $R-
  558.           Range checking can be performed on all array and string subscript
  559.           values.  If the index is out of range, then a run time error occurs
  560.           and the program halts.  Set this option to enable range checking, and
  561.           disable this option to turn off range checking.  Programs may run
  562.           noticeably faster with range checking turned off, and will require
  563.           somewhat less code.
  564.  
  565. Stack checking: $S, default $S+
  566.           When this option is set, the compiler generates extra code for every
  567.           procedure and function call to insure that adequate memory remains in
  568.           the stack area.  If the call would cause the program to run out of
  569.           memory, the program is halted with a run-time error.
  570.  
  571. Strict var-strings: $V, default $V+
  572.           Most Pascal compilers will not let you pass a string variable as a 
  573.           var parameter unless both  var parameters are exactly the same type
  574.           and length.  For example, you normally cannot pass a STRING[80] typed
  575.           variable to a procedure having a var parameter defined (via a type
  576.           declaration) as STRING[128].  However, by selecting the $V- option,
  577.           Turbo Pascal allow you to pass mismatched string types as procedure
  578.           parameters.
  579.  
  580. Extended syntax: $X, default $X-
  581.           When $X+ is in effect, Turbo Pascal lets you call a function just
  582.           like it was a procedure.  In effect, the result of the function will
  583.           be ignored and thrown away.
  584.           This feature does not apply to any System unit functions.
  585.  
  586.  
  587. Disk File Operations
  588.      Turbo Pascal provides disk file access through file declarations, standard
  589. library procedures, functions and system variables.  Turbo Pascal programs can
  590. read and write text files (containing text information), as well as reading and
  591. writing random access disk files containing complex data records.
  592.      This section describes basic file operations, and then presents example
  593. code for reading and writing text files, reading and writing sequential and
  594. random access data files, and an example using the high speed BlockRead and
  595. BlockWrite file functions.
  596.  
  597.  
  598. Defining a File Identifier
  599.      Text files are accessed through a special data type called Text, such that
  600. a file identifier is declared as,
  601.  
  602.      var
  603.        TextFileID : Text;
  604.  
  605. Data files (those containing data other than text files) are accessed via a
  606. File identifier declared using the syntax,
  607.  
  608.      var
  609.        FileId : File of <type>;
  610.  
  611. where <type> is any type identifier, including built in data types, records and
  612. user defined types.  Some examples:
  613.  
  614.      type
  615.        TPersonInfo = record
  616.                Name : String[20];
  617.                Age : Integer;
  618.        end;
  619.      var
  620.        DataFile1 : File of Real;
  621.        DataFile2 : File of Integer;
  622.        DataFile3 : File of TPersonInfo;
  623.  
  624.  
  625. Opening a File
  626.      A filename is associated with a file identifier by calling the Assign
  627. procedure, like this:
  628.  
  629.      Assign ( DataFile1, 'QTR-DATA.DAT' );
  630.  
  631. which assigns the filename 'QTR-DATA.DAT' to file identifier DataFile1.  This,
  632. however, does not actually open the file.
  633.      To open an existing file, use the Reset procedure (or the Append
  634. procedure, summarized later):
  635.  
  636.      Reset ( DataFile1 );
  637.  
  638. To create a new file and open it so that data can be written to the file,
  639. write:
  640.  
  641.      Rewrite ( DataFile1 );
  642.  
  643.  
  644. Writing and Reading File Data
  645.      Data is output to a data file using the standard Write procedures and
  646. specifying the file identifier as the first parameter.  Example:
  647.  
  648.      Write ( DataFile1, 3.14159 );
  649.  
  650. Each time data is output to the file, an internal file pointer is advanced
  651. forward into the file.  When a file is first opened, the file pointer is
  652. located at the beginning of the file.  Once a record is written to the file,
  653. the file pointer advances to just beyond that record, so that it points to the
  654. next position in the file.
  655.      If a previously created file was opened using Reset, data may be read by
  656. using Read, specifying the file identifier as the first identifier, followed by
  657. a data variable to store the results.  Example:
  658.  
  659.      Read ( DataFile1, X );
  660.  
  661.  
  662. Checking for File-related Errors
  663.      By default, if a file operation error is detected, such as unable to open
  664. a file, the program halts with a run-time error.  However, your program can
  665. disable automatic file error detection and process file errors directly by
  666. checking the value returned by the IOResult function.
  667.      To test for file errors, you must set the {$I-} option to disable
  668. automatic I/O (input/output) result checking.  Then, perform the desired
  669. option.  Example:
  670.  
  671.      {$I-}
  672.      Reset( DataFile1 );
  673.      {$I+}
  674.      ErrorCode := IOResult;
  675.      if  ErrorCode <> 0  then
  676.      begin
  677.        Writeln('Problem opening the file');
  678.        ...
  679.      end;
  680.  
  681.      While you can test the value of IOResult directly, you will probably want
  682. to copy its integer result to a variable, as shown in the code fragment above. 
  683. This is because IOResult returns the result code of the previous I/O operation,
  684. and then resets the internal error code to 0.  Consequently, if you test
  685. IOResult in a conditional statement, and then attempt to access the error code
  686. again to display an error message or perform other processing, IOResult will
  687. now be zero.
  688.  
  689.  
  690. Text Files
  691.      Listing 3.12 demonstrates reading and writing text files.  This simple
  692. program prompts for a filename to read from, and the name of file to copy to. 
  693. It opens the input file, InFile using the Reset procedure.  The output file,
  694. OutFile is opened using Rewrite to create the new file.  However, to insure
  695. that an existing file is not inadvertently overwritten, the program first
  696. attempts to open the file using the Reset procedure.  If the IOResult is zero,
  697. this means that the file already exists, so the program prompts to see if you
  698. really wish to overwrite the existing file.  Then the file is opened using the
  699. Rewrite procedure.
  700.      Finally, a while loop reads each line from InFile, and writes it to
  701. OutFile, displaying a dot or period '.' on the screen for each line that is
  702. processed, providing user feedback that the program is doing something.  (Its a
  703. good idea to present some type of feedback whenever your program performs time
  704. consuming tasks.  If nothing happens for too long, the user might suspect the
  705. program has crashed, or perhaps is reformatting the hard disk, neither of which
  706. makes for user friendly software.)
  707.      The while loop calls the function Eof, which returns True when the end of
  708. the input file has been reached.  If the program did not call Eof, it would
  709. eventually attempt to read beyond the end of the file, resulting in a run-time
  710. error.  While not shown in this example, you could disable I/O result checking
  711. using {$I-} and check the IOResult function after each Read and Write.
  712.      You may wish to modify this program to perform various types of data
  713. manipulation.  For instance, you can easily alter this program to create a
  714. program that puts line numbers on Pascal source statements.  Add a TotalLines
  715. line counter variable, set to zero, and then increment by 1 for each line read
  716. from the input file.  Add TotalLines to the Writeln output statement, like
  717. this:
  718.  
  719.      Writeln( OutFile, TotalLines:4, ': ', DataLine );
  720. To send output to the printer, instead of another disk file, specify LPT1: (or
  721. other appropriate DOS printer device) for the output file name.
  722.  
  723. Listing 3.12.  An example program showing how process text files.
  724.    1  program TextFiles; {TEXTFILE.PAS}
  725.    2  { Demonstrates copying a text file to another file }
  726.    3  
  727.    4  var
  728.    5    DataLine    : String;
  729.    6    Error       : Integer;
  730.    7    InFileName  : String[80];
  731.    8    InFile      : Text;
  732.    9    Line        : Integer;
  733.   10    OutFileName : String[80];
  734.   11    OutFile     : Text;
  735.   12    TotalLines  : Word;
  736.   13    Response    : String[80];
  737.   14  
  738.   15  
  739.   16  begin
  740.   17  
  741.   18    { Get name of input file and open the file }
  742.   19    Repeat
  743.   20  
  744.   21      Write('Enter the name of the file to read (CR=done): ');
  745.   22      Readln( InFileName );
  746.   23      if  Length( InFileName ) > 0  then
  747.   24      begin
  748.   25        Assign( InFile, InFileName );
  749.   26        {$I-}
  750.   27        Reset( InFile );
  751.   28        {$I+}
  752.   29        Error := IoResult;
  753.   30        if  Error <> 0 then
  754.   31          writeln('Unable to open ',InfileName,'.');
  755.   32      end; { if begin }
  756.   33  
  757.   34    Until (Error = 0) or (Length( InFileName ) = 0);
  758.   35  
  759.   36  
  760.   37    { Get name of output file and open the file }
  761.   38    If  Length( InFileName ) > 0 then
  762.   39    begin
  763.   40  
  764.   41      repeat
  765.   42        repeat
  766.   43          Error := 0;
  767.   44          Write('Enter the name of the file to COPY TO (CR=done): ');
  768.   45          Readln( OutFileName );
  769.   46          if  Length( OutFileName ) > 0 then
  770.   47          begin
  771.   48            Assign( OutFile, OutFileName );
  772.   49            {$I-}
  773.   50            Reset( OutFile );
  774.   51            {$I+}
  775.   52            Error := IoResult;
  776.   53            if  Error = 0  then
  777.   54            begin
  778.   55              Close ( OutFile );
  779.   56              Write(OutFileName,' already exists.  Overwrite (Y/Cr=No)? ');
  780.   57              Readln( Response );
  781.   58              if  (Response = 'Y') or (Response = 'y') then
  782.   59                Error := 1; { Allow exit from the filename query loop }
  783.   60            end; { if begin }
  784.   61          end; { if begin }
  785.   62        until (Error <> 0) or (Length( OutFileName ) = 0);
  786.   63  
  787.   64        if  Error <> 0  then
  788.   65        begin
  789.   66          {$I-}
  790.   67          Rewrite( OutFile );
  791.   68          {$I+}
  792.   69  
  793.   70          Error := IoResult;
  794.   71          if  Error <> 0  then
  795.   72            Writeln('Problem creating ',OutFileName,'.');
  796.   73        end; { if begin }
  797.   74  
  798.   75      until (Error = 0) or (Length( OutFileName ) = 0);
  799.   76  
  800.   77  
  801.   78      { Copy the input file to the output file }
  802.   79      if  Length( OutFileName ) > 0  then
  803.   80      begin
  804.   81  
  805.   82        while not Eof(InFile) do
  806.   83        begin
  807.   84          Readln( InFile, DataLine );
  808.   85          Writeln( OutFile, DataLine );
  809.   86  
  810.   87          { Write a dot on the screen for each line copied }
  811.   88          Write('.');
  812.   89        end; { while }
  813.   90        Writeln;
  814.   91  
  815.   92        Close( InFile );
  816.   93        Close( OutFile );
  817.   94      end; { if begin }
  818.   95  
  819.   96    end; { if begin }
  820.   97  
  821.   98  end. { program }
  822.  
  823. Important Note:  Other Text File Features
  824.      Additional text file procedures and facilities are provided in Turbo
  825. Pascal.  The Append procedure operates like Reset, to open an existing file,
  826. but automatically positions the file pointer to the end of the file so that the
  827. next Write operation will be made to the end of the file.  Normally, text file
  828. I/O is performed through a 128-byte sized internal file buffer maintained by
  829. Turbo Pascal.   For faster performance, this default buffer may be replaced
  830. with a larger buffer using the SetTextBuf procedure.  Normally, the buffer is
  831. only output to the disk when it becomes full, but programs can force the buffer
  832. to be emptied to disk by calling the Flush procedure.  These routines are
  833. briefly described below in the section Other File Operations, and described in
  834. detail in the library reference section.
  835.  
  836.  
  837. Sequential Access Data Files
  838.      A frequent use of file operations is reading and writing record-oriented
  839. data.  In this form, the entire contents of a record structure, including all
  840. of its fields, are written to the disk file.  Data values within the record,
  841. such as integer or real data types, are stored on the disk file in their
  842. internal format.  Listing 3.13 illustrates by creating a small array of a
  843. TDataRecord, containing name, phone number and age values.  The contents of
  844. each record are written directly to the data file with the statement,
  845.  
  846.      for I := 0 to MaxRecords do
  847.        Write( SeqFile, DataRecords[I] );
  848.  
  849. After each record is written, the internal file pointer advances to the next
  850. position in the file.  In this example program, the data file is then closed
  851. and reopened for reading, using Reset.  Entire records are then read from the
  852. file using,
  853.  
  854.      Read ( SeqFile, SampleRecord );
  855.  
  856. Each record is written back, in sequence.  Hence, this particular data file is
  857. accessed as a sequential data file.  Turbo Pascal also provides random access
  858. data files, permitting reading and writing of specific records anywhere within
  859. the file.
  860.  
  861. Listing 3.13.  An example program that writes and then reads data to and from a
  862. sequential file.
  863.    1  { SEQFILE.PAS }
  864.    2  program SeqFiles;
  865.    3  { Demonstrates writing data to a sequential record file
  866.    4    and reading it back in }
  867.    5  
  868.    6  const
  869.    7    MaxRecords = 4;
  870.    8  
  871.    9  type
  872.   10    TDataRecord = record
  873.   11                    Name        : String[20];
  874.   12                    PhoneNumber : String[14];
  875.   13                    Age         : Integer;
  876.   14                  end;
  877.   15  
  878.   16  const
  879.   17    DataRecords : Array[0..MaxRecords] of TDataRecord
  880.   18  
  881.   19                = ( (Name : 'George'; PhoneNumber : '262-1234'; Age : 10 ),
  882.   20                    (Name : 'John'  ; PhoneNumber : '262-1235'; Age : 20 ),
  883.   21                    (Name : 'Lisa'  ; PhoneNumber : '262-1236'; Age : 22 ),
  884.   22                    (Name : 'Marcia'; PhoneNumber : '262-1237'; Age : 30 ),
  885.   23                    (Name : 'Gwen'  ; PhoneNumber : '262-1238'; Age : 4 )
  886. );
  887.   24  
  888.   25  
  889.   26  var
  890.   27    I           : Integer;
  891.   28    SeqFile     : File of TDataRecord;
  892.   29    SampleRecord: TDataRecord;
  893.   30  
  894.   31  begin
  895.   32    Assign( SeqFile, 'SEQFILE.DAT');
  896.   33    Rewrite( SeqFile );
  897.   34  
  898.   35    for I := 0 to MaxRecords  do
  899.   36      Write( SeqFile, DataRecords[I] );
  900.   37  
  901.   38    Close ( SeqFile );
  902.   39  
  903.   40    Reset( SeqFile );
  904.   41    For I := 0 to MaxRecords do
  905.   42    begin
  906.   43      Read( SeqFile, SampleRecord );
  907.   44      with  SampleRecord  do
  908.   45        Writeln('Name=', Name,', Phone #=', PhoneNumber, ', Age=', Age);
  909.   46    end;
  910.   47  
  911.   48    Close ( SeqFile );
  912.   49  
  913.   50    Write('Press Enter when done.');
  914.   51    Readln;
  915.   52  
  916.   53  
  917.   54  end. { program }
  918.  
  919.  
  920. Random Access Data Files
  921.      Program RandFile, in listing 3.14 demonstrates the use of random access
  922. data files to read and write any record within the data file.  The process is
  923. nearly the same as a sequential data file except that a new procedure, Seek, is
  924. used to position the internal file pointer to specific records in the file. 
  925. Seek has two parameters: the file identifier and the record number (each data
  926. record within the file counts as a single record).  A statement such as,
  927.  
  928.      Seek ( RandomFile, 10 );
  929.  
  930. positions the internal file pointer to the 10th record in the data file.  The
  931. next read (or write) will occur at this location in the file.
  932.  
  933.  
  934. Listing 3.14.  How to use random access file operations.
  935.    1  program RandFile; {RANDFILE.PAS}
  936.    2  { Demonstrates random access to a record file }
  937.    3  
  938.    4  const
  939.    5    MaxRecords = 100;
  940.    6  
  941.    7  type
  942.    8    TDataRecord = record
  943.    9                    Name        : String[20];
  944.   10                    PhoneNumber : String[14];
  945.   11                    Age         : Integer;
  946.   12                    Available   : Boolean;
  947.   13                  end;
  948.   14  
  949.   15  var
  950.   16    Command     : Char;
  951.   17    RandomFile  : File of TDataRecord;
  952.   18  
  953.   19  
  954.   20  
  955.   21  procedure AddRecord;
  956.   22  var
  957.   23    DataRecord : TDataRecord;
  958.   24    NewName : String[20];
  959.   25    NewPhone: String[14];
  960.   26    NewAge  : String[20];
  961.   27    RecordNum  : Word;
  962.   28  
  963.   29  
  964.   30  function  FindFreeRecord : Integer;
  965.   31  { Scans through the file until finding a free or unused record.
  966.   32    Returns:  The record number of the free record, or if no free
  967.   33              records are found, then returns MaxRecords + 1.
  968.   34  }
  969.   35  var
  970.   36    DataRecord : TDataRecord;
  971.   37    RecNum : Integer;
  972.   38  begin
  973.   39    RecNum := 0;
  974.   40    repeat
  975.   41      Seek( RandomFile, RecNum );
  976.   42      Read( RandomFile, DataRecord );
  977.   43      if  not  DataRecord.Available  then
  978.   44        RecNum := RecNum + 1;
  979.   45    until (RecNum > MaxRecords) Or (DataRecord.Available);
  980.   46    FindFreeRecord := RecNum;
  981.   47  end; { FindFreeRecord }
  982.   48  
  983.   49  
  984.   50  begin { AddRecord }
  985.   51  
  986.   52    RecordNum := FindFreeRecord;
  987.   53    if  RecordNum > MaxRecords  then
  988.   54      Writeln('The maximum number of records (',MaxRecords,') are in use.')
  989.   55    else
  990.   56    begin
  991.   57  
  992.   58      with  DataRecord  do
  993.   59      begin
  994.   60        Writeln('Adding record #', RecordNum );
  995.   61        Write('Enter Name ?');
  996.   62        Readln( Name );
  997.   63        Write('Enter # PhoneNumber ?');
  998.   64        Readln( PhoneNumber );
  999.   65        Write('Enter Age ?');
  1000.   66        Readln( Age );
  1001.   67        Available := False;
  1002.   68      end;
  1003.   69  
  1004.   70      Seek( Randomfile, RecordNum );
  1005.   71      Write( RandomFile, DataRecord );
  1006.   72    end;
  1007.   73  
  1008.   74  end; { AddRecord }
  1009.   75  
  1010.   76  
  1011.   77  procedure DisplayRecords;
  1012.   78  var
  1013.   79    DataRecord : TDataRecord;
  1014.   80    I : Integer;
  1015.   81  begin
  1016.   82    for  I := 0 to MaxRecords  do
  1017.   83    begin
  1018.   84      Seek( Randomfile, I );
  1019.   85      Read( RandomFile, DataRecord );
  1020.   86      if  not DataRecord.Available  then
  1021.   87      { Found a record that is in use }
  1022.   88      begin
  1023.   89        Writeln('Record #', I);
  1024.   90        with  DataRecord  do
  1025.   91          Writeln('Name=', Name,', Phone #=', PhoneNumber,', Age=', Age:3);
  1026.   92        Writeln;
  1027.   93      end; { if begin }
  1028.   94    end; { for begin }
  1029.   95  end; { DisplayRecords }
  1030.   96  
  1031.   97  
  1032.   98  procedure EditRecord;
  1033.   99  var
  1034.  100    DataRecord : TDataRecord;
  1035.  101    Error : Integer;
  1036.  102    NewName : String[20];
  1037.  103    NewPhone: String[14];
  1038.  104    NewAge  : String[20];
  1039.  105    RecordNum  : Word;
  1040.  106  begin
  1041.  107  
  1042.  108    Repeat
  1043.  109  
  1044.  110      Write('Enter number of record to edit:  ');
  1045.  111      Readln( RecordNum );
  1046.  112  
  1047.  113      Seek( RandomFile, RecordNum );
  1048.  114      Read( RandomFile, DataRecord);
  1049.  115      if  DataRecord.Available  then
  1050.  116        writeln('Record #', RecordNum, ' does not contain any data.');
  1051.  117  
  1052.  118    Until  not DataRecord.Available;
  1053.  119  
  1054.  120  
  1055.  121    with  DataRecord  do
  1056.  122    begin
  1057.  123      Write('Name =', Name,'(CR=no change) ? ');
  1058.  124      Readln( NewName );
  1059.  125      if  NewName <> '' then
  1060.  126        Name := NewName;
  1061.  127      Write('PhoneNumber =', PhoneNumber, '(CR=no change) ? ');
  1062.  128      Readln( NewPhone );
  1063.  129      if  NewPhone <> ''  then
  1064.  130        PhoneNumber := NewPhone;
  1065.  131      Write('Age =', Age, '(CR=no change) ? ');
  1066.  132      Readln( NewAge );
  1067.  133      if  NewAge <> ''  then
  1068.  134        Val( NewAge, Age, Error);
  1069.  135    end;
  1070.  136    Seek( RandomFile, RecordNum );
  1071.  137    Write( RandomFile, DataRecord );
  1072.  138  end; { EditRecord }
  1073.  139  
  1074.  140  
  1075.  141  procedure OpenFile;
  1076.  142  var
  1077.  143    SampleRecord : TDataRecord;
  1078.  144    Error : Integer;
  1079.  145    I : Integer;
  1080.  146  begin
  1081.  147    { Open existing file or create a new random access data file }
  1082.  148    Assign( RandomFile, 'RANDFILE.DAT');
  1083.  149    {$I-}
  1084.  150    Reset( RandomFile );
  1085.  151    {$I+}
  1086.  152    Error := IoResult;
  1087.  153    if  Error <> 0  then
  1088.  154    begin
  1089.  155      { Create and initialize a new file.  Set all records to available }
  1090.  156      Rewrite( RandomFile );
  1091.  157      SampleRecord.Available := True;
  1092.  158      for  I := 0 to MaxRecords  do
  1093.  159        write( RandomFile, SampleRecord );
  1094.  160    end; { if begin }
  1095.  161  end; { OpenFile }
  1096.  162  
  1097.  163  
  1098.  164  begin { RandFile }
  1099.  165  
  1100.  166    OpenFile;
  1101.  167  
  1102.  168    repeat
  1103.  169      Write('A)dd record, D)isplay records, E)dit record, Q)uit? ');
  1104.  170      Readln( Command );
  1105.  171      Command := Upcase( Command );
  1106.  172      case  Command  of
  1107.  173        'A' :  AddRecord;
  1108.  174        'D' :  DisplayRecords;
  1109.  175        'E' :  EditRecord;
  1110.  176      end; { case }
  1111.  177    until Command = 'Q';
  1112.  178  
  1113.  179    Close( RandomFile );
  1114.  180  
  1115.  181  
  1116.  182    Write('Press Enter when done.');
  1117.  183    Readln;
  1118.  184  
  1119.  185  
  1120.  186  end. { program }
  1121.  
  1122.      The actual data record is defined in TDataRecord, containing 4 fields: 
  1123. Name, PhoneNumber, Age, and a Boolean flag Available.  The Available flag is
  1124. used to keep track of which records are in use and which have yet to be used.
  1125.      Procedure OpenFile first tries to open an existing 'RANDFILE.DAT', since
  1126. if the file already exists, it will then be used as-is.  However, if no such
  1127. file exists, a new one is created by calling Rewrite.  For each of the records
  1128. in file (the total number of records is specified in the MaxRecords constant),
  1129. the Available flag is set to True, using this code:
  1130.  
  1131.      SampleRecord.Available := True;
  1132.      for I := 0 to MaxRecords do
  1133.        write( RandomFile, SampleRecord );
  1134.  
  1135. Note that we don't care about the contents of the other fields.  As long as the
  1136. Available flag is set to True, we know that the record is available for writing
  1137. new data to, and we will ignore the contents of the other fields.
  1138.      In the main body of the program, a repeat-until loop is used to prompt for
  1139. a command, A)dd, D)isplay, E)dit or Q)uit the program (this notation is used to
  1140. suggest that you should only enter the first letter of each command).  After
  1141. converting to upper case, the program uses a case statement to determine which
  1142. procedure should execute.
  1143.      AddRecord calls its local function FindFreeRecord to find the next free
  1144. record (one with Available = True).  FindFreeRecord merely reads through the
  1145. file, one record at a time, until either finding a free record, or reaching the
  1146. end of the data file.  Back inside AddRecord, the user is prompted for a new
  1147. name, phone number and age values; the Available flag is set to False and the
  1148. record is written out to the file.  Note the use of Seek to position to the
  1149. free record in the file, and then a Write procedure to output the record data
  1150. to the file.
  1151.      The EditRecord procedure takes a record number as input, seeks to the
  1152. given record, reads the data from the file, and gives you a chance to make
  1153. changes.  The altered data is then output to the file.
  1154.      DisplayRecords scans through the file, displaying the record number, name,
  1155. phone number and age values for each record that is in use.
  1156.      For practice, you may wish to modify this program to include a Delete
  1157. function.  All that's needed is to output a new record with the Available flag
  1158. set to True, for each record that is deleted.
  1159.  
  1160. Important note:  Writing Pointer values to disk files
  1161.      When writing a record structure to a disk file, any pointer type values
  1162. contained in the record are also written to the file.  However, when these
  1163. values are read back into memory from a disk file, their value is meaningless. 
  1164. Pointers to internal memory addresses are only valid when used within the
  1165. original execution of the program.
  1166.      Instead of storing pointers in the disk file, you must be able to recreate
  1167. the internal data structure without referencing the pointer values.  A simple
  1168. approach is to output the data records in the exact order that they will be
  1169. read back.  Upon reading each record from the file, you must recreate the
  1170. internal data strcuture, via calls to New to allocate new data records from the
  1171. heap, and then explicitly reassign any pointer values to the new allocations.
  1172.      More complicated structures may require that you create a file record
  1173. number field, and instead of using pointer values in the data file, store
  1174. actual file record numbers.  Upon reading the file, you would then use New to
  1175. create a new internal record, and copy the values read from disk into the
  1176. record, setting pointers as appropriate, to point to other records in the
  1177. internal memory structure.
  1178.  
  1179.  
  1180. BlockRead and BlockWrite:  The Use of Untyped Files
  1181.      In addition to reading and writing specific data types, Turbo Pascal
  1182. provides for untyped data files, using just the File keyword, and two
  1183. procedures BlockRead and BlockWrite, for reading and writing data to such a
  1184. file.  By default, each untyped files is treated as having 128 bytes per block
  1185. (but can be set to other values).  This means that data is read or written in
  1186. 128-byte sized chuncks called blocks.
  1187.      BlockRead and BlockWrite are especially useful for performing high speed
  1188. file copy operations because of their ability to read and write very large
  1189. blocks of data in a single statement.  Block I/O is also used by programs that
  1190. perform their own internal data formatting.  For example, a word processor may
  1191. store its internal representation of a document directly to disk using
  1192. BlockWrite to output the document's internal memory structure.  Data base and
  1193. spreadsheet applications may also build buffers of data for faster reading and
  1194. writing of data base and spreadsheet files.
  1195.      An untyped file is declared using the File keyword by itself, as in this
  1196. declaration of InFile and OutFile:
  1197.  
  1198.      var
  1199.        InFile, OutFile : File;
  1200.  
  1201. BlockRead has 3 or 4 parameters, depending on its mode of operation.  The same
  1202. is true of BlockWrite.  At a minimum, each procedure has 3 parameters:
  1203.  
  1204.      BlockRead( FileId, Buffer, NumBlocks );
  1205.      BlockWrite( FileId, Buffer, NumBlocks );
  1206.  
  1207. where,
  1208.  
  1209.        FileId is a file identifier specifying the file where the operation will
  1210.        be performed,
  1211.  
  1212.        Buffer, is typically an array of char or an array of byte large enough
  1213.        to hold all the data,
  1214.  
  1215.        NumBlocks is the number of blocks to read into the buffer, where each
  1216.        block is either the default of 128 bytes, or is a different block size
  1217.        as specified with the Reset  or Rewrite procedures (see below).
  1218.  
  1219.        An optional 4th parameter returns the number of blocks actually read,
  1220.        which, in the event that something prevented the program from reading
  1221.        the number of blocks that were requested, may be different than the
  1222.        number of blocks requested.  For example, a program might request 4
  1223.        blocks but only read 3 blocks before reaching the end of the file.  In
  1224.        this case, the 4th parameter returns a value of 3.
  1225.      An example, given the definition of Buffer as:
  1226.  
  1227.      var
  1228.        Buffer : Array [0..2047] of Char;
  1229.  
  1230. then, the statement,
  1231.  
  1232.      BlockRead ( FileId, Buffer, 16 );
  1233.  
  1234. reads sixteen 128 byte-sized blocks from the file and places them in Buffer.
  1235.  
  1236.  
  1237. Specifying Different Block Sizes
  1238.      The Reset and Rewrite procedures have an optional 2nd parameter to specify
  1239. a different block size.  For example,
  1240.  
  1241.      Reset ( FileId, 1024 );
  1242.  
  1243. specifies that each block of data read from the untyped FileId will contain
  1244. 1024 bytes of data.  In this instance, you may write,
  1245.  
  1246.      BlockRead ( FileId, Buffer, 2 );
  1247.  
  1248. and read two 1,204 byte blocks from the disk file.
  1249.      A problem with BlockRead is that it cannot read data that is less than the
  1250. block size.  For instance, what happens if you attempt to read a 1,204 byte
  1251. sized block from a file having only 300 bytes?  If you use BlockRead with the
  1252. optional 4th parameter, as for instance,
  1253.  
  1254.      BlockRead ( FileId, Buffer, 1, Result );
  1255.  
  1256. then Result will be 0, indicating that the amount of data you requested could
  1257. not be read.  The partial record is stored in Buffer, but you have no way of
  1258. knowing how many bytes were actually read.  
  1259.      The solution to this problem is to open the file with a block size of 1
  1260. byte, and set the NumBlocks parameter to the Buffer size.  For example,
  1261.  
  1262.      Reset ( FileId, 1 );
  1263.      BlockRead ( FileId, Buffer, SizeOf(Buffer), Result );
  1264.  
  1265. Now, if the amount of data to read is less than 2048 bytes, the actual number
  1266. read is returned in Result.  Listing 3.15 is a procedure CopyFile that copies
  1267. one file to another, using a blocksize of 1, and buffer size of 1,204 bytes
  1268. (see the declaration of TFileBuffer).  For faster performance you can change
  1269. the declaration of TFileBuffer to a much larger size, such as 32,768 bytes.   
  1270.      As with other file operations, use the {$I-} compiler option to disable
  1271. automatic I/O result checking.  Write your own error checking routines to check
  1272. the contents of IOResult to determine success or failure of block operations.
  1273.  
  1274.  
  1275. Listing 3.15.  Demonstration of the BlockRead and BlockWrite functions for fast
  1276. file copying. 
  1277.    1  function CopyAFile ( Source, Dest : String ) : Integer;
  1278.    2  {COPYFILE.PAS}
  1279.    3  { Purpose:
  1280.    4    Copies the filenamed in 'Source' to the file named in 'Dest'.
  1281.    5  
  1282.    6    Returns:
  1283.    7    0 = copy was okay
  1284.    8    1 = not enough RAM memory to copy the file
  1285.    9    2 = error occurred when writing to the 'Dest' file
  1286.   10    3 = Could not open the source file
  1287.   11    4 = Could not open the destination file
  1288.   12  }
  1289.   13  label
  1290.   14    ExitProc;
  1291.   15  
  1292.   16  type
  1293.   17    TFileBuffer = Array [0..1023] of Byte;
  1294.   18      { Copies the file in 1k chunks.  For a larger buffer, 
  1295.   19        increase the size of the byte array. }
  1296.   20  var
  1297.   21    BytesIn  : Integer;   { Number of bytes read in }
  1298.   22    BytesOut : Integer;   { Number of bytes written out }
  1299.   23    F1 : File;            { F1=file to read, F2=file to write }
  1300.   24    F2 : File;
  1301.   25    FileBuffer : ^TFileBuffer;
  1302.   26    Dialog : PDialog;
  1303.   27    Bounds : TRect;
  1304.   28  
  1305.   29  begin { CopyFile }
  1306.   30  
  1307.   31    New( FileBuffer );
  1308.   32    if  FileBuffer = NIL  then
  1309.   33      CopyAFile := 1 {Not enough RAM memory to do a copy}
  1310.   34    else
  1311.   35    begin
  1312.   36  
  1313.   37      { Open the source file }
  1314.   38      Assign ( F1, Source );
  1315.   39      {$I-}
  1316.   40      Reset ( F1, 1 );
  1317.   41      if  IoResult <> 0  then
  1318.   42      begin
  1319.   43        CopyAFile := 3; {Error on opening source file}
  1320.   44        goto ExitProc;
  1321.   45      end;
  1322.   46  
  1323.   47      { Open the destination file }
  1324.   48      Assign ( F2, Dest );
  1325.   49      Rewrite ( F2, 1 );
  1326.   50      if  IoResult <> 0  then
  1327.   51      begin
  1328.   52        CopyAFile := 4; {Error on opening the destination file}
  1329.   53        goto ExitProc;
  1330.   54      end; { if }
  1331.   55  
  1332.   56      repeat
  1333.   57        BlockRead ( F1, FileBuffer^, SizeOf(FileBuffer^), BytesIn );
  1334.   58        if  BytesIn > 0  then
  1335.   59        begin
  1336.   60          { Since we read something, go ahead and write it to the
  1337. destination }
  1338.   61          BlockWrite ( F2, FileBuffer^, BytesIn, BytesOut );
  1339.   62          if  BytesIn <> BytesOut  then
  1340.   63          begin
  1341.   64            CopyAFile := 2; {Error occurred while writing the output}
  1342.   65            {$I-}
  1343.   66            Close ( F2 );
  1344.   67            Erase ( F2 );
  1345.   68            Close ( F1 );
  1346.   69            {$I+}
  1347.   70            goto ExitProc;
  1348.   71          end; { begin }
  1349.   72        end; { if }
  1350.   73      until  BytesIn = 0;
  1351.   74  
  1352.   75      CopyAFile := 0;
  1353.   76      Close ( F1 );
  1354.   77      Close ( F2 );
  1355.   78  
  1356.   79    end; { begin }
  1357.   80  
  1358.   81  ExitProc:
  1359.   82  
  1360.   83    if  FileBuffer <> NIL  then
  1361.   84      Dispose (FileBuffer);
  1362.   85  
  1363.   86  end; { CopyAFile  }
  1364.   87  
  1365.  
  1366. Important Note: Maximum buffer size and maximum data to read or write
  1367.      The maximum amount of data that can be read with BlockRead or written with
  1368. BlockWrite is limited in the following ways:
  1369.        Individual data structures in Turbo Pascal can be no larger than 65,521
  1370.        bytes.
  1371.        The file record size times the count of bytes to read or write, must be
  1372.        65,535 (or less) since its stored in a Word data type.  In other words,
  1373.        SizeOf(Buffer) * NumBlocks must be less than or equal to 65,535.
  1374.  
  1375.  
  1376. Other File Operations
  1377.      In addition to the basic file operation facilities covered in the previous
  1378. sections, Turbo Pascal provides a variety of standard procedures for creating
  1379. directories, switching to other directories, erasing and renaming files and so
  1380. on.  Other functions and variables provide additional features.  The following
  1381. is a summary of the available functions; they are described in detail in the
  1382. library reference section.
  1383.  
  1384.  
  1385. Procedures
  1386.  
  1387.        ChDir ( S: String ):  Changes the current directory to the path
  1388.        specified by S.
  1389.  
  1390.        Erase ( var F): Erases or deletes the file associated with the closed
  1391.        file identifier F (with the name previously given to it by Assign), 
  1392.  
  1393.        GetDir (D: Byte; var S: String):  Where D=1 for drive A, 2 for drive B,
  1394.        etc, or 0 for the current drive, GetDir returns the current directory
  1395.        name in variable S.
  1396.  
  1397.        MkDir( S: String ):  Used to create new subdirectories, where S is the
  1398.        path, including the subdirectory name, to create.
  1399.  
  1400.        Rename( var F; Newname : String ):  Where F is a closed file identifier
  1401.        having a name set with the Assign procedure, Rename changes the name of
  1402.        that file to the name specified by Newname.
  1403.  
  1404.        RmDir( S : String ):  Deletes an empty subdirectory specified by S.
  1405.  
  1406.        Truncate( var F ):  The current file position in open file F becomes the
  1407.        new end of file, effectively disgarding any records that may follow the
  1408.        current record.
  1409.  
  1410.  
  1411. Functions
  1412.  
  1413.        FilePos (var F): Longint:  Returns the current position in the non-text
  1414.        file as the record number of the file pointer.
  1415.  
  1416.        FileSize (var F): Longint :  Where F is an open non-text file, this
  1417.        returns the size of the file, in number of components.  If F is a file
  1418.        of Char, then FileSize(F) returns the size in bytes.  If  F is a file of
  1419.        Integer, then FileSize(F) returns the number of integer records in the
  1420.        file.
  1421.  
  1422.  
  1423. Text file procedures
  1424.  
  1425.        Append (var F: Text): Like Reset, for opening an existing text file, but
  1426.        automatically positions the current file position to the end of the file
  1427.        so that the next output will be written at the end of the current file.
  1428.  
  1429.        Flush( var F: Text ):  Empties the internal buffer associated with text
  1430.        file F. to the disk file.
  1431.  
  1432.        SetTextBuf(var F: Text; var Buf [; Size : Word ] ):  By default, all
  1433.        text files use an internal 128 byte-sized buffer.  For faster text file
  1434.        performance, programs can establish their own text file buffer by
  1435.        calling SetTextBuf and passing to it their own data block to be used for
  1436.        the buffer.  SizeOf(Buf) becomes the new buffer size, or a different
  1437.        size may be explicitly provided with the optional Size parameter.  Calls
  1438.        to SetTextBuf should occur just prior to Append, Reset or Rewrite.
  1439.  
  1440.        Eoln ( var F : Text ): Returns True if the next character is the text
  1441.        file is an end of line character (e.g. carriage return).
  1442.  
  1443.        SeekEof( var F: Text ):  For text file use only, SeekEof is equivalent
  1444.        to the Eof function except it skips over any trailing blanks, tabs or
  1445.        blank lines.
  1446.  
  1447.        SeekEoln( var F: Text ):  For text file use only, SeekEoln is equivalent
  1448.        to Eoln, except that is skips over trailing blanks and tabs at the end
  1449.        of a line.
  1450.  
  1451.  
  1452. Untyped Files
  1453.  
  1454.        FileMode:  By assigning a value to FileMode prior to opening a file with
  1455.        Reset, you can specify an access mode for the file.  A value of 0
  1456.        specifies Read only, 1 specifies Write only, and 2, the default value,
  1457.        specifies Read/write access to the file.  The value of FileMode remains
  1458.        in effect for all subsequent Reset operations, until set to some other
  1459.        value.
  1460.  
  1461.  
  1462.  
  1463. Turbo Pascal Memory Limitations
  1464.  
  1465.        Maximum statement width:  126 characters
  1466.  
  1467.        Maximum size of generated code per code unit: 64k bytes
  1468.        The program module and each Unit may contain up to 64k bytes of code. 
  1469.        Ifthe program or unit exceeds this value, then you must split it up into
  1470.        smaller sections.
  1471.  
  1472.        Maximum size of global and typed constants: 64k bytes
  1473.        If your program must have several large values, such as arrays, as
  1474.        global values, and their combined size will exceed this value, then you
  1475.        must recode your program to reduce the memory usage.  A simple approach
  1476.        is to dynamically allocate the arrays and define only a pointer to the
  1477.        array as a global value.
  1478.  
  1479.        Maximum program stack size (used for procedure and function call return
  1480.        address and all local variable storage):  64k bytes
  1481.        The default stack size is 16k bytes but may be adjusted up or down using
  1482.        the $M compiler directive or the IDE's Options/Memory Sizes menu
  1483.        selection.
  1484.  
  1485.        Maximum heap size:  0 to 640k bytes
  1486.        The heap's default memory allocation is all the memory left over after
  1487.        the code is loaded and the stack and data segments are allocated.  The
  1488.        heap will take all the available memory up to its maximum declared size,
  1489.        which is 640k by default.  You will want to set the default value to a
  1490.        lower limit especially if your program will be memory resident with
  1491.        other software, or if your program launches other applications using the
  1492.        Exec procedure.
  1493.  
  1494.        Maximum data allocation size: 65,521 bytes
  1495.        You cannot allocate a single data structure (such as an array) that is
  1496.        larger than this size.
  1497.  
  1498.